
#pragma once

#include <stdint.h>
#include <vector>
#include <mutex>
#include <thread>

namespace FlameEngine
{
	class MemChunk
	{
	public:
		MemChunk(size_t blockSize, uint8_t count);
		~MemChunk();
		void* Allocate(uint32_t blockSize);
		void Deallocate(void* p, size_t blockSize);
		inline uint8_t GetFreeCount() { return m_freeBlockCount; }
		inline bool HasPointer(void* p,uint32_t chunkSize)
		{
			return ((p >= m_pData) && (p < m_pData + chunkSize));
		}

	private:
		uint8_t* m_pData;
		uint8_t m_firstFreeBlock;
		uint8_t m_freeBlockCount;
	};

	class FixedSizeAllocator
	{
	public:
		FixedSizeAllocator(size_t blockSize,uint8_t maxBlockPerChunk=0xff);
		virtual ~FixedSizeAllocator();

		virtual void* Allocate();
		virtual void Deallocate(void* p);

		int32_t GetEmptyCount() { return m_totalEmptyBlockCount; }
		int32_t GetPoolSize() { return m_blockSize * m_maxBlockPerChunk * m_memChunks.size(); }

		FixedSizeAllocator() = delete;
		FixedSizeAllocator(FixedSizeAllocator&) = delete;
		FixedSizeAllocator& operator= (FixedSizeAllocator&) = delete;

	protected:
		void* AllocateImp();
		void DeallocateImp(void* p);

	private:
		const size_t m_blockSize;
		const size_t m_chunkSize;
		uint8_t m_maxBlockPerChunk;
		MemChunk* m_pAllocChunk;
		MemChunk* m_pDeallocChunk;
		std::vector<MemChunk*> m_memChunks;
		int32_t m_totalEmptyBlockCount;
		std::thread::id m_threadId;
	};

	class FixedSizeAllocatorThreadSafe:public FixedSizeAllocator
	{
	public:
		FixedSizeAllocatorThreadSafe(size_t blockSize, uint8_t maxBlockPerChunk = 0xff);
		~FixedSizeAllocatorThreadSafe();

		virtual void* Allocate();
		virtual void Deallocate(void* p);
		
		FixedSizeAllocatorThreadSafe() = delete;
		FixedSizeAllocatorThreadSafe(FixedSizeAllocatorThreadSafe&) = delete;
		FixedSizeAllocatorThreadSafe& operator= (FixedSizeAllocatorThreadSafe&) = delete;

	private:
		std::mutex m_mutex;
	};
}